/**
 * Copyright (c) 2019-2021,  陈俊兵 (cjb6b.com@gmail.com)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ktjiaoyu.advance.threadlocal;


import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @packageName com.ktjiaoyu.advance.threadlocal
 * @fileName ThreadLocalWeakTest.java
 * @author 陈俊兵 (cjb6b.com@gmail.com)
 * @createTime 2023/7/27 16:23
 * @lastModify 2023/7/27 16:23
 * @version 1.0.0
 */
public class ThreadLocalWeakTest {

    static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        List<Integer> val = new ArrayList<>();

        new Thread(()->{
            threadLocal.set("test");
            // 通过反射获取ThreadLocal对象
            ThreadLocal local = getMyLocal();
            // 强引用还在，则ThreadLocal对象不会释放
            System.out.println(local == threadLocal);

            System.out.println("直接获取数据：" + threadLocal.get());
            // 将数据移除掉，就能保证数据被回收
            threadLocal.remove();
            local = null;
            threadLocal = null;
            // threadLocal的强引用被去除，则弱引用也会被释放
            System.gc();
            local = getMyLocal();
            System.out.println(local);

            Object myData = getMyData();
            // 可以看到数据依然可以获取到，即使threadLocal都释放掉了
            System.out.println("反射获取数据：" + myData);
        }).start();
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();
    }

    public static ThreadLocal getMyLocal(){
        Thread thread = Thread.currentThread();
        try {
            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object o = field.get(thread);
            Field tableField = o.getClass().getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(o);

            int length = Array.getLength(table);
            for (int i = 0; i < length; i++) {
                Object entry = Array.get(table, i);
                if(entry != null){
                    Method get = entry.getClass().getMethod("get");
                    get.setAccessible(true);
                    Object tl = get.invoke(entry);
                    return (ThreadLocal) tl;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object getMyData(){
        Thread thread = Thread.currentThread();
        try {
            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object o = field.get(thread);
            Field tableField = o.getClass().getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(o);

            int length = Array.getLength(table);
            for (int i = 0; i < length; i++) {
                Object entry = Array.get(table, i);
                if(entry != null){
                    Field valueField = entry.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object tl = valueField.get(entry);
                    return tl;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
